#!/usr/bin/python
# -*- coding: utf-8 -*-

# =======================================
# rssappend v0.2b - Copyright 2012
# Writted by muttley
# Get last version from code.google.com/p/unuseful-code/
# =======================================

import sys
from datetime import datetime, timedelta
from xml.dom import minidom
from optparse import OptionParser, OptionGroup

""" TODO: 
- Si dovrebbero poter togliere tutte le u davanti le stringhe dato che ho usato sys.setdefaultencoding('UTF-8')"""

# set 'UTF-8' standart default encoding
reload(sys)
sys.setdefaultencoding('UTF-8')

# GLOBAL VAR
g_verbosity = 0
g_max_item = None

def main ():
    
    global g_verbosity, g_max_item
    
    # Help message
    usage       = u"usage: %prog [-m MAXITEM] [-v] -t TITLE [-l LINK] [-d DESCRIPTION] [-a AUTHOR] [-c CATEGORY] [-g GUID [-k]] [-p PUBDATE] <rss-local-file.xml>\n\n"\
                  u"       vedi http://feed2.w3.org/docs/rss2.html per informazioni sui tags.\n\n"\
                  u"   es: %prog -t \"Titolo dell'articolo da aggiungere\" /path/to/rss-local-file\n"\
                  u"       %prog -m 10 -t \"Titolo dell'articolo da aggiungere\" -d \"Descrizione dell'articolo\" -l \"http://link/to/article\" -v /path/to/rss-local-file"
                              
    description = u"Aggiunge un item ad un rss file esistente. "\
                  u"Impostando un MAXITEM, gli articoli più vecchi verranno cancellati."
    version     = u" %prog 0.2b"    # --version to print
    epilog      = u" Copyright 2012"
    
    # Program parsed option
    
    parser = OptionParser(usage=usage, description=description, epilog=epilog, version=version)
    
    parser.add_option("-m", "--max-item", type="int", dest="maxItem", default=None, 
                      help="numero di articoli (<item/>) massimi da mantenere.")
    
    parser.add_option("-v", "--verbose", action="count", dest="verbosity", default=g_verbosity,
                      help="Abilita (ed incrementa) la verbosita. Opzione ripetibile.")
    
    group = OptionGroup(parser, "Articolo (<item/>) child tags", "Tags che caratterizzano l'articolo da aggiungere. Maggiori informazioni su: http://feed2.w3.org/docs/rss2.html")
                      
    group.add_option("-t", "--title", type="string", dest="itemTitle", default=None, metavar="TITLE")
                      
    group.add_option("-l", "--link", type="string", dest="itemLink", default=None, metavar="LINK")
                      
    group.add_option("-d", "--description", type="string", dest="itemDescription", default=None, metavar="DESCRIPTION")
    
    group.add_option("-a", "--author", type="string", dest="itemAuthor", default=None, metavar="AUTHOR")
                    
    group.add_option("-c", "--category", type="string", dest="itemCategory", default=None, metavar="CATEGORY")
                      
    group.add_option("-g", "--guid", type="string", dest="itemGuid", default=None, metavar="GUID")
    
    group.add_option("-k", "--permaLink", action="store_false", dest="permaLink", default=True,
                     help="Attributo 'isPermaLink' del tag <guid> settato a false (true per default)")
                      
    # Default=current utc/gmt datetime formatted
    group.add_option("-p", "--pub-date", type="string", dest="itemPubDate", default=datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT"), metavar="PUDATE",
                     help=u"Sarà utilizzata anche per aggiornare il tag <lastBuildDate>. Se non indicata la data sarà quella corrente di sistema.")
    
    parser.add_option_group(group)
    
    (options, args) = parser.parse_args()
    
    # Manage param and error
    if len(args) != 1:
        myPrint( clr( 'ERROR: ', 4) + 'Indica un <rss-local-file.xml>!\n\n', STR_STDERR )
        parser.print_help()
        sys.exit(1)
    
    if options.itemTitle is None:
        myPrint( clr( 'ERROR: ', 4) + 'Almeno il titolo dell\'articolo va indicato!\n\n', STR_STDERR )
        parser.print_help()
        sys.exit(1)
    
    # Init global variable
    g_verbosity = options.verbosity
    g_max_item = options.maxItem
    rssLocalFile = args[0]
    
    #myPrint( clr( "INFO.01: ", 1 ) + "Verbosity set to %02d\n" % g_verbosity, STR_STDOUT, 1, g_verbosity) 
    
    # EDIT XML ===

    xmldoc = minidom.parse(rssLocalFile)
    
    if (xmldoc):
    
        # Get <pubDate> nodes
        pubDateNodes = xmldoc.documentElement.getElementsByTagName('pubDate')
        itemNodes = xmldoc.documentElement.getElementsByTagName('item')
        
        # If <item> or <pubDate> node is not present
        if (itemNodes.length is 0):
            myPrint( clr( "ERROR: ", 4 ) + "Non c'è nessun <item> tags in %s...almeno uno deve essere presente!\n" % rssLocalFile, STR_STDERR)
            sys.exit(1)
        elif (pubDateNodes.length is 0):
            myPrint( clr( "ERROR: ", 4 ) + "Non c'è nessun <pubDate> tags in %s...necessari per eliminare gli articoli più vecchi!\n" % rssLocalFile, STR_STDERR)
            sys.exit(1)
            
        # ParentNode of item (node to delete) => channel in rss 2.0
        parentOfItem = itemNodes[0].parentNode
        
        # Remove old <item> by <pubDate> tag
        # If g_max_item is set and <item> nodes are more of g_max_item
        if (g_max_item is not None and pubDateNodes.length > g_max_item - 1):
            
            myPrint( clr( "INFO.01: ", 1 ) + "Rimozione articoli...\n", STR_STDOUT, 1, g_verbosity)
            
            pubDatesDict = {}
            
            # Iterate on <pubDate> nodes
            for node in pubDateNodes:
                
                # Use only <pubDate> in <item> 
                if( node.parentNode.localName != 'item' ): continue
                
                # Get node value 
                nodeValue = getNodeTxtValue( node )
                
                if ( nodeValue ):
                    # Parse date format (es. Sat, 08 Sep 2003 21:49:00 GMT) and retrive datetime obj
                    pubDate = datetime.strptime(nodeValue, '%a, %d %b %Y %H:%M:%S %Z')
                    
                    # Just have equal date in dict (brutta pezza...)
                    while pubDate in pubDatesDict:
                        # Add a second for not 'overwrite' pre-existent key in dict
                        pubDate += timedelta(0,1)
                        
                    pubDatesDict[pubDate] = node
                        
            # Number of item to remove ('-1' beacause i'll add new one...)
            nItemToRemove = len(pubDatesDict) - (g_max_item - 1)

            # Only if there is some node to remove            
            if ( nItemToRemove > 0 ):
                # Remove older <item> respected the g_max_item
                for key in sorted(pubDatesDict.iterkeys()):
                    myPrint( clr( "INFO.02: ", 1 ) + " - item con pubDate: %s\n" % getNodeTxtValue(pubDatesDict[key]), STR_STDOUT, 2, g_verbosity) 
                        
                    # Remove 'item'
                    parentOfItem.removeChild(pubDatesDict[key].parentNode)
                    pubDatesDict[key].parentNode.unlink()
                    
                    nItemToRemove -= 1
                    if(nItemToRemove is 0): break

        # ADD ITEM TO XML ===

        # Create the <item> base element
        newItem = xmldoc.createElement("item")
        
        # Get the first item node (just have itemNodes, but i could remove the first...)
        firstItemNode = xmldoc.documentElement.getElementsByTagName('item')[0]
        
        # Insert new <item> on top of others 
        parentOfItem.insertBefore(newItem, firstItemNode)
        
        # Tuple of item childs
        itemChildsToInsert = ( ('title', options.itemTitle), ('link', options.itemLink), 
                               ('description', options.itemDescription), ('author', options.itemAuthor), 
                               ('category', options.itemCategory), ('guid', options.itemGuid),
                               ('pubDate', options.itemPubDate) )
        
        myPrint( clr( "INFO.01: ", 1 ) + "Aggiunta nuovo articolo... ( title: "+clr(options.itemTitle, 2)+" )\n", STR_STDOUT, 1, g_verbosity)
        
        # Insert item childs
        for child in itemChildsToInsert:
            if( child[1] is not None ):
                myPrint( clr( "INFO.02: ", 1 ) + " + item child ( "+child[0]+": "+clr(child[1], 2)+" )\n", STR_STDOUT, 2, g_verbosity)
                
                # Create a <child> element and insert into <item> element
                childElement = xmldoc.createElement(child[0])
                if(child[0] is 'guid'): 
                    if( options.permaLink is True ):
                        childElement.setAttribute("isPermaLink", "true")
                    else:
                        childElement.setAttribute("isPermaLink", "false")
                newItem.appendChild(childElement)
                
                # Create a TextNode and insert into <child> element
                childElementText = xmldoc.createTextNode(child[1])
                childElement.appendChild(childElementText)
        
        myPrint( clr( "INFO.02: ", 1 ) + "Update <lastBuildDate> tag: "+clr(options.itemPubDate, 2)+" (same of <pubDate>)\n", STR_STDOUT, 2, g_verbosity)
        
        # Update <lastBuildDate> 'global' tag with <puDate> tag content
        lastBuildDateNode = xmldoc.documentElement.getElementsByTagName('lastBuildDate')[0]
        childElementText = xmldoc.createTextNode(options.itemPubDate)
        lastBuildDateNode.replaceChild(childElementText, lastBuildDateNode.childNodes[0])
        
        myPrint( clr( "INFO.01: ", 1 ) + "Salvataggio delle modifiche nel file: " + clr( rssLocalFile, 2 ) + '\n', STR_STDOUT, 1, g_verbosity)              
        #xml = xmldoc.toxml(encoding="utf-8")
        
        # Write file
        file = open(rssLocalFile, 'w')
        xmldoc.writexml( file )
        file.close()
                
    else:
        myPrint( clr( "ERROR: ", 4 ) + "Problemi nell'apertura del fil %s!\n" % rssLocalFile, STR_STDERR)
        sys.exit(1)
    
    # Exit without errors
    sys.exit(0)

# Get text value of node
# es: <tag>TextValue returned</tag>
# @node: object - node
# @return: string - value of node
def getNodeTxtValue ( node ):
    
    for text_node in node.childNodes:
       value = ""
       if (text_node.nodeType == node.TEXT_NODE):
           value += text_node.nodeValue
       # Print the title if we have one
       if (len(value) > 0):
           return value
       else:
           return False

STR_STDOUT = 0
STR_STDERR = 1
# Print on screen follow verbosity and stream type
# @text: string - text to print
# @streamType: int - STR_STDOUT or STR_STDERR
# @verbosity: int - verbosity associed to text
# @requestVerbosity: int - request verbosity
# @return: bool - return if printed
def myPrint( text, streamType = STR_STDOUT, verbosity = 1 , requestVerbosity = 1 ):
    
    if verbosity <= requestVerbosity:
        if streamType == STR_STDOUT:
            sys.stdout.write( text )
        else: 
            sys.stderr.write( text )
        return True
    else:
        return False
        
        
G_COLOR = ( '\033[95m', '\033[94m', '\033[92m', '\033[93m', '\033[91m' )
G_COLOR_ENDC = '\033[0m'
# Colorize string
# @string: string - text to colorize
# @color: int - color type, see G_COLOR tuple
# @return: string - return colored string
def clr( string, color ):
    
    return G_COLOR[color] + string + G_COLOR_ENDC

if __name__ == "__main__":
    main()

